﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Network.Interface;
using Network.Interface.Packets;

namespace Network.Client
{
    public class NetworkClient<TCp, TSp> : IDisposable
        where TCp: ClientPacket
        where TSp: ServerPacket
    {
        public NetworkClient(string hostName, int port)
        {
            Client = new UdpClient(hostName, port);
            PacketsToSend = new Queue<TCp>();
            SendThread = new Thread(Send);
            SendThread.Start();
            GetThread = new Thread(Get);
            GetThread.Start();
        }

        private TSp mPacket;

        public int PacketsInQueue
        {
            get { return PacketsToSend.Count; }
        }

        public bool TryGetPacket(out TSp packet)
        {
            bool result = false;
            packet = null;
            if(mPacket != null)
            {
                packet = mPacket;
                mPacket = null;
                result = true;
            }
            return result;
        }

        public void Send(TCp packet)
        {
            PacketsToSend.Enqueue(packet);
        }

        public void KillClient()
        {
            DropConnection = true;
            Client.Close();
        }

        protected bool DropConnection;
        protected readonly Thread GetThread;
        protected readonly Thread SendThread;
        protected readonly UdpClient Client;
        protected readonly Queue<TCp> PacketsToSend;

        protected virtual void Send()
        {
            while (!DropConnection)
            {
                if(PacketsToSend.Count > 0)
                {
                    try
                    {
                        byte[] packet = SerializationHelper.ObjectToByteArray(PacketsToSend.Dequeue());
                        Client.Send(packet, packet.Length);
                    }
                    catch (SocketException)
                    {
                        return;
                    }
                    
                }
                else
                    Thread.Sleep(5);
            }
        }

        protected virtual void Get()
        {
            IPEndPoint remote = new IPEndPoint(IPAddress.None, 0);
            while (!DropConnection)
            {
                try
                {
                    mPacket = SerializationHelper.ByteArrayToObject<TSp>(Client.Receive(ref remote));
                }
                catch (SocketException)
                {
                    return;
                }
            }
        }

        public void Dispose()
        {
            if(Client != null)
                ((IDisposable)Client).Dispose();
            if(GetThread.ThreadState == ThreadState.Running)
                GetThread.Abort();
            if (SendThread.ThreadState == ThreadState.Running)
                SendThread.Abort();
        }
    }
}
